/* Copyright (c) 2009 Richard Lincoln */ package com.cimphony.cimtoole.ecore; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.emf.ecore.EAnnotation; import org.eclipse.emf.ecore.EAttribute; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EDataType; import org.eclipse.emf.ecore.EEnum; import org.eclipse.emf.ecore.EEnumLiteral; import org.eclipse.emf.ecore.EModelElement; import org.eclipse.emf.ecore.ENamedElement; import org.eclipse.emf.ecore.EPackage; import org.eclipse.emf.ecore.EReference; import org.eclipse.emf.ecore.EcoreFactory; import org.eclipse.emf.ecore.EcorePackage; import au.com.langdale.cimtoole.project.Task; import au.com.langdale.kena.OntModel; import au.com.langdale.kena.OntResource; import au.com.langdale.kena.ResIterator; import au.com.langdale.profiles.ProfileClass; import au.com.langdale.profiles.ProfileClass.PropertyInfo; import au.com.langdale.profiles.SchemaGenerator; import au.com.langdale.xmi.UML; import com.cimphony.cimtoole.util.CIMToolEcoreUtil; public class EcoreGenerator extends SchemaGenerator { public class Index{ public Map<String, EPackage> ePackages = new HashMap<String, EPackage>(); // uri to EPackage public Map<String, EClass> eClasses = new HashMap<String, EClass>(); // uri to EClass public Map<String, EAttribute> eAttributes = new HashMap<String, EAttribute>(); public Map<String, EReference> eReferences = new HashMap<String, EReference>(); public Map<String, EEnum> eEnums = new HashMap<String, EEnum>(); public Map<String, EDataType> eDataTypes = new HashMap<String, EDataType>(); public Map<String, EDataType> eTypes = new HashMap<String, EDataType>(); // xsdtype to ecore public ArrayList<EReference> notInverted = new ArrayList<EReference>(); public EPackage root; } protected String namespace, profileNamespace; protected boolean addRootClass; public static final String ELEMENT_CLASS_NAME = "Element"; public static final String ELEMENT_CLASS_IDENTIFIER = "UUID"; public static final String RDF_SERIALISATION_ANNOTATION = "http://cimphony.com/rdf/2010/serialisation"; public static final String PROFILE_ANNOTATION = "http://cimphony.com/profiles/2010/profile"; public static final String UML_NS = UML.NS.substring(0, UML.NS.length()-1); EcoreFactory coreFactory = EcoreFactory.eINSTANCE; EcorePackage corePackage = EcorePackage.eINSTANCE; // EPackage result = coreFactory.createEPackage(); protected Index index; protected boolean merged, preserveNamespaces; protected String originalNamespace, originalProfileNamespace; protected IProject project; protected boolean isEcoreSchema(){ IFolder folder = Task.getSchemaFolder(project); try{ for (IResource res : folder.members()){ if (res.getName().endsWith(".ecore") || res.getName().endsWith(".ecore-registry")) return true; } }catch (CoreException ex){ ex.printStackTrace(); } return false; } public EcoreGenerator(OntModel profileModel, OntModel backgroundModel, String namespace, String profileNamespace, boolean preserveNamespaces, boolean inverses, boolean addRootClass, IProject project) { this(profileModel, backgroundModel, namespace, profileNamespace, preserveNamespaces, inverses, addRootClass, project, false); } public EcoreGenerator(OntModel profileModel, OntModel backgroundModel, String namespace, String profileNamespace, boolean preserveNamespaces, boolean inverses, boolean addRootClass, IProject project, boolean merged) { super(profileModel, backgroundModel, preserveNamespaces, inverses); this.project = project; this.merged = merged; this.index = new Index(); // index.root = result; this.originalNamespace = namespace; this.originalProfileNamespace = profileNamespace; this.addRootClass = addRootClass; this.profileNamespace = profileNamespace; this.preserveNamespaces = preserveNamespaces; this.index.eTypes.putAll(CIMToolEcoreUtil.getEDataTypeMap()); if (namespace.endsWith("#")) namespace = namespace.substring(0, namespace.length()-1); if (profileNamespace.endsWith("#")) profileNamespace = profileNamespace.substring(0, profileNamespace.length()-1); if (namespace!=null && profileNamespace!=null && preserveNamespaces && !merged) this.namespace = namespace+"?profile="+profileNamespace; else this.namespace = namespace; } public EPackage getResult() { return index.root;//result; } @SuppressWarnings("unchecked") @Override protected void scanProfiles() { if (!merged){ super.scanProfiles(); return; } Iterator<?> it = getProfileClasses(profileModel, model, merged); while( it.hasNext()) work.add(it.next()); while( ! work.isEmpty()) { ProfileClass profile = (ProfileClass) work.remove(0); scanProperties(profile); OntResource base = profile.getBaseClass(); if( base == null) { log("No base for profile class", profile.getSubject()); } else { catalog.add(base, profile.getSubject()); if((profile.isEnumerated() || profile.isRestrictedEnum()) && ! profile.isUnion()) enums.add(base, profile.getIndividuals()); } } } @Override protected boolean scanProperties(ProfileClass profile) { if (!merged){ return super.scanProperties(profile); } return scanForResource(profile.getBaseClass(), profile); } @SuppressWarnings("unchecked") protected boolean scanForResource(OntResource res, ProfileClass profileClass){ ResIterator[] iterators = new ResIterator[]{ profileModel.listObjectProperties(), profileModel.listDatatypeProperties() }; boolean some = false; for (ResIterator it : iterators){ some = some | it.hasNext(); while( it.hasNext()) { OntResource next = (OntResource)it.next(); if (next.getDomain().equals(res)){ PropertyInfo info = profileClass.getPropertyInfo((OntResource)next); ProfileClass range_profile = props.add( info ); if( range_profile != null) work.add(range_profile); }else{ // System.out.println(next.toString() + next.getDomain()); } } } return some; } public static Iterator<?> getProfileClasses(final OntModel profileModel, final OntModel fullModel, final boolean merged) { return new Iterator<Object>() { List<?> classes = ProfileClass.getNamedProfiles(profileModel, fullModel); int ix; public boolean hasNext() { return ix < classes.size(); } public Object next() { OntResource clss = (OntResource)classes.get(ix++); if (merged) return new ProfileClass(clss, clss.getNameSpace(), clss); else return new ProfileClass(clss, clss.getNameSpace()); } public void remove() { } }; } /* * Adds packages and classifiers without parent packages to the 'result' package. * Create an Element class from which all other classes derive. */ @Override public void run() { super.run(); EPackage result = null; Collection<EPackage> roots = new HashSet<EPackage>(); for (EPackage p : index.ePackages.values()){ if (p.getESuperPackage() == null && p.eContents().size()>0){ roots.add(p); } } if (roots.size() == 1) result = roots.iterator().next(); else{ result = EcoreFactory.eINSTANCE.createEPackage(); result.getESubpackages().addAll(roots); } index.root = result; if (originalNamespace.endsWith("#")){ EAnnotation annotation = EcoreFactory.eINSTANCE.createEAnnotation(); annotation.setSource(RDF_SERIALISATION_ANNOTATION); annotation.getDetails().put("suffix", "#"); index.root.getEAnnotations().add(annotation); } if (!originalNamespace.equals(originalProfileNamespace)){ EAnnotation pAnnotation = EcoreFactory.eINSTANCE.createEAnnotation(); pAnnotation.setSource(PROFILE_ANNOTATION); pAnnotation.getDetails().put("nsURI", profileNamespace); index.root.getEAnnotations().add(pAnnotation); } index.root.setNsPrefix("cim"); index.root.setNsURI(this.namespace); Iterator<?> nt = datatypes.iterator(); while( nt.hasNext()) { OntResource type = (OntResource)nt.next(); EDataType dt = index.eDataTypes.get(type.getURI()); if (dt!= null && type.getIsDefinedBy() != null){ EPackage p = index.ePackages.get(type.getIsDefinedBy().getURI()); if (p!=null) p.getEClassifiers().add(dt); } } /* Create root Element class from which all other classes derive. */ EClass element = coreFactory.createEClass(); if (addRootClass) { if (index.root.getEClassifier(ELEMENT_CLASS_NAME)!=null && index.root.getEClassifier(ELEMENT_CLASS_NAME) instanceof EClass) element = (EClass)index.root.getEClassifier(ELEMENT_CLASS_NAME); else element.setName(EcoreGenerator.ELEMENT_CLASS_NAME); element.setAbstract(true); EAttribute uri; if (element.getEStructuralFeature(ELEMENT_CLASS_IDENTIFIER) == null || !(element.getEStructuralFeature(ELEMENT_CLASS_IDENTIFIER) instanceof EAttribute)){ uri = coreFactory.createEAttribute(); uri.setName(EcoreGenerator.ELEMENT_CLASS_IDENTIFIER); uri.setEType(corePackage.getEString()); element.getEStructuralFeatures().add(uri); }else uri = (EAttribute)element.getEStructuralFeature(ELEMENT_CLASS_IDENTIFIER); uri.setID(true); index.root.getEClassifiers().add(element); } for (Iterator<EClass> ix = index.eClasses.values().iterator(); ix.hasNext();) { EClass klass = ix.next(); if (klass.getEPackage() == null) index.root.getEClassifiers().add(klass); /* Make all classes derive from Element. */ if (addRootClass && (klass.getESuperTypes().size() == 0) && klass!=element && !isCompound(klass)) { klass.getESuperTypes().add(element); } for (EReference ref : klass.getEReferences()){ if (ref.getName()==null){ String name = ref.getEType().getName(); if (ref.isMany()) name+="s"; log("Reference between "+klass.getName()+" and "+ ref.getEType().getName()+" has no role name, setting to "+name); ref.setName(name); } } } for (Iterator<EEnum> ix = index.eEnums.values().iterator(); ix.hasNext();) { EEnum eEnum= ix.next(); if (eEnum.getEPackage() == null) index.root.getEClassifiers().add(eEnum); } for (Iterator<EDataType> ix = index.eDataTypes.values().iterator(); ix.hasNext();) { EDataType dt = ix.next(); if (dt.getEPackage() == null) index.root.getEClassifiers().add(dt); } for (Iterator<EDataType> ix = index.eTypes.values().iterator(); ix.hasNext();) { EDataType dt = ix.next(); if (dt.getEPackage() == null) index.root.getEClassifiers().add(dt); } for (Iterator<EPackage> ix = index.ePackages.values().iterator(); ix.hasNext();) { EPackage pkg = ix.next(); if (pkg.getESuperPackage() == null && pkg != index.root){ index.root.getESubpackages().add(pkg); } } if (!isEcoreSchema()){ for (Iterator<EPackage> ix = index.ePackages.values().iterator(); ix.hasNext();) { EPackage pkg = ix.next(); if (pkg.getESuperPackage() == index.root && index.root.getESubpackages().size()==1 && pkg.getESubpackages().size()==1){ index.root.getESubpackages().addAll(pkg.getESubpackages()); index.root.getEClassifiers().addAll(pkg.getEClassifiers()); index.root.getEAnnotations().addAll(pkg.getEAnnotations()); if (index.root.getName() == null) index.root.setName(pkg.getName()); index.root.getESubpackages().remove(pkg); } } } for (Iterator<EReference> ix = index.notInverted.iterator(); ix.hasNext();) { EReference ref = ix.next(); if (!isCompound((EClass)ref.getEType())) log("Non-inverted reference: " + ref.getName()); } } @Override protected void emitPackage(String uri) { if (!uri.equals(UML.global_package.getURI())){ EPackage pkg = coreFactory.createEPackage(); index.ePackages.put(uri, pkg); } } @Override protected void emitClass(String uri, String base) { EClass klass = coreFactory.createEClass(); // Assume abstract unless 'concrete' stereotype emitted. if (!merged) klass.setAbstract(true); index.eClasses.put(uri, klass); } @Override protected void emitDefinedBy(String uri, String container) { if (index.ePackages.containsKey(container)) { EPackage parent = index.ePackages.get(container); if (index.ePackages.containsKey(uri)) { EPackage child = index.ePackages.get(uri); parent.getESubpackages().add(child); } else if (index.eClasses.containsKey(uri)) { EClass child = index.eClasses.get(uri); parent.getEClassifiers().add(child); } else if (index.eDataTypes.containsKey(uri)) { EDataType child = index.eDataTypes.get(uri); parent.getEClassifiers().add(child); } else if (index.eEnums.containsKey(uri)) { EEnum child = index.eEnums.get(uri); parent.getEClassifiers().add(child); } else { log("Problem location contained [" + container + "] element [" + uri + "]."); } } else if (!container.equals(UML.global_package.getURI())){ log("Container [" + container + "] for " + uri + " not found."); } } @Override protected void emitDatatype(String uri, String xsdtype) { EDataType dt = coreFactory.createEDataType(); EAnnotation profileAnnotation = coreFactory.createEAnnotation(); profileAnnotation.setSource("http:///org/eclipse/emf/ecore/util/ExtendedMetaData"); profileAnnotation.getDetails().put("baseType", xsdtype); dt.getEAnnotations().add(profileAnnotation); if (CIMToolEcoreUtil.getTypeClass(xsdtype)!=null) { dt.setInstanceTypeName(CIMToolEcoreUtil.getTypeClass(xsdtype).toString()); dt.setInstanceClass(CIMToolEcoreUtil.getTypeClass(xsdtype)); } else { log("Data type [" + xsdtype + "] not found."); dt.setInstanceClass(Object.class); } index.eDataTypes.put(uri, dt); } @Override protected void emitDatatypeProperty(String uri, String base, String domain, String type, String xsdtype, boolean required) { EAttribute attr = coreFactory.createEAttribute(); if (index.eDataTypes.containsKey(type)) { EDataType dt = index.eDataTypes.get(type); attr.setEType(dt); } else if (index.eTypes.containsKey(xsdtype)) { attr.setEType(index.eTypes.get(xsdtype)); } else { log("No EType [" + xsdtype + "] found for " + uri + "."); } if (!merged && required == true) attr.setLowerBound(1); attr.setUpperBound(1); attr.setUnsettable(true); if (index.eClasses.containsKey(domain)) { EClass klass = index.eClasses.get(domain); klass.getEStructuralFeatures().add(attr); } else { log("Problem locating class [" + uri + "] for attribute [" + type + "]."); } index.eAttributes.put(uri, attr); } /* * Aggregation (has a). * http://iec.ch/TC57/2009/CIM-schema-cim14#VoltageLevel, http://langdale.com.au/2005/UML#ofAggregate * * Composition (owns a). If the container is destroyed, normally every instance that it contains is destroyed as well. * http://langdale.com.au/2005/UML#compositeOf * * Changes the element from a nested structure to a reference. * http://iec.ch/TC57/2009/CIM-schema-cim14#VoltageLevel, http://langdale.com.au/2005/UML#byreference * * Normally, any structured class that has no subclasses would be marked concrete. * http://iec.ch/TC57/2009/CIM-schema-cim14#VoltageLevel, http://langdale.com.au/2005/UML#concrete */ @Override protected void emitStereotype(String uri, String stereo) { if (index.eClasses.containsKey(uri)) { EClass klass = index.eClasses.get(uri); if (stereo.equals(UML.concrete.toString())) { klass.setAbstract(false); } } else { log("Problem locating stereotype [" + stereo + "] class [" + uri + "]."); } } /* * Enumerations are emitted as classes and must be converted to EEnums when * the base stereotype is emitted. Instances for the enumeration get stored * as attributes of the class before being converted to EEnumLiterals. The * EAttributes are also stored in the list of all attributes for labelling. */ @Override protected void emitInstance(String uri, String base, String type) { if (index.eClasses.containsKey(type)) { EClass klass = index.eClasses.get(type); EAttribute attr = coreFactory.createEAttribute(); klass.getEStructuralFeatures().add(attr); index.eAttributes.put(uri, attr); } else { log("Problem locating class [" + type + "] for instance [" + uri + "]"); } } @Override protected void emitBaseStereotype(String uri, String stereo) { // Convert classes with enumeration base sterotypes to EEnums. if ((stereo.equals(UML.enumeration.toString())) && index.eClasses.containsKey(uri)) { EClass klass = index.eClasses.get(uri); EEnum eEnum = coreFactory.createEEnum(); eEnum.setName(klass.getName()); Integer j = new Integer(0); for (Iterator<EAttribute> ix = klass.getEAttributes().iterator(); ix.hasNext();) { EAttribute attr = ix.next(); EEnumLiteral literal = coreFactory.createEEnumLiteral(); literal.setName(attr.getName()); literal.setValue(j); eEnum.getELiterals().add(literal); index.eAttributes.remove(uri + "." + attr.getName()); j++; } index.eEnums.put(uri, eEnum); // Substitute the class with the enumeration. index.eClasses.remove(uri); } else if (stereo.equals(UML.compound.toString())) { if (index.eClasses.containsKey(uri)){ System.out.println(uri +" is compound"); EClass cls = index.eClasses.get(uri); if (cls.getEAnnotation(UML_NS) == null){ EAnnotation profileAnnotation = coreFactory.createEAnnotation(); profileAnnotation.setSource(UML_NS); cls.getEAnnotations().add(profileAnnotation); } cls.getEAnnotation(UML_NS).getDetails().put("CIMDataType", "Compound"); } // ref.setContainment(true); } } protected boolean isCompound(EClass cls){ if (cls.getEAnnotation(UML_NS)==null) return false; if (cls.getEAnnotation(UML_NS).getDetails().get("CIMDataType")==null) return false; if (cls.getEAnnotation(UML_NS).getDetails().get("CIMDataType").equals("Compound")) return true; return false; } @Override protected void emitObjectProperty(String uri, String base, String domain, String range, boolean required, boolean functional) { if (index.eClasses.containsKey(domain) && index.eClasses.containsKey(range)) { EReference ref = coreFactory.createEReference(); EClass klass = index.eClasses.get(domain); klass.getEStructuralFeatures().add(ref); EClass referenced = index.eClasses.get(range); ref.setEType(referenced); if (!merged && required == true) ref.setLowerBound(1); if (functional == false) ref.setUpperBound(-1); if (isCompound(referenced)) ref.setContainment(true); index.eReferences.put(uri, ref); } else if (index.eClasses.containsKey(domain) && index.eEnums.containsKey(range)) { EAttribute attr = coreFactory.createEAttribute(); EClass klass = index.eClasses.get(domain); klass.getEStructuralFeatures().add(attr); EEnum eEnum = index.eEnums.get(range); attr.setEType(eEnum); attr.setUnsettable(true); if (!merged && required == true) attr.setLowerBound(1); index.eAttributes.put(uri, attr); } else { log("Problem locating classes [" + domain + ", " + range + "] for reference [" + uri + "]."); } } @Override protected void emitInverse(String uri, String iuri) { if (index.eReferences.containsKey(uri) && index.eReferences.containsKey(iuri)) { index.eReferences.get(uri).setEOpposite(index.eReferences.get(iuri)); index.eReferences.get(iuri).setEOpposite(index.eReferences.get(uri)); index.notInverted.remove(index.eReferences.get(uri)); index.notInverted.remove(index.eReferences.get(iuri)); } else if (index.eReferences.containsKey(uri)) { index.notInverted.add(index.eReferences.get(uri)); } else if (index.eReferences.containsKey(iuri)) { index.notInverted.add(index.eReferences.get(iuri)); } else { log("Problem inverting " + uri + " and " + iuri + "."); } } @Override protected void emitRestriction(String uri, String domain, String range) { // Do nothing } @Override protected void emitRestriction(String uri, String domain, boolean required, boolean functional) { //Do nothing } @Override protected void emitSuperClass(String subClass, String superClass) { if (index.eClasses.containsKey(subClass) && index.eClasses.containsKey(superClass)) { index.eClasses.get(subClass).getESuperTypes().add(index.eClasses.get(superClass)); } else { log("Error setting super type [" + superClass + "] for " + subClass + "."); } } @Override protected void emitLabel(String uri, String label) { ENamedElement named = null; if (index.ePackages.containsKey(uri)) { named = index.ePackages.get(uri); EPackage pkg = (EPackage)named; if (namespace.endsWith("#")) pkg.setNsURI(namespace+label); else pkg.setNsURI(namespace+"#"+label); pkg.setNsPrefix("cim"+label); } else if (index.eClasses.containsKey(uri)) { named = index.eClasses.get(uri); } else if (index.eAttributes.containsKey(uri)) { named = index.eAttributes.get(uri); } else if (index.eReferences.containsKey(uri)) { named = index.eReferences.get(uri); } else if (index.eEnums.containsKey(uri)) { named = index.eEnums.get(uri); } else if (index.eDataTypes.containsKey(uri)) { named = index.eDataTypes.get(uri); } else { log("Problem applying [" + uri +"] label: " + label); } if (named != null) named.setName(label); } @Override protected void emitComment(String uri, String baseComment, String profileComment) { EModelElement annotated = null; if (index.ePackages.containsKey(uri)) { annotated = index.ePackages.get(uri); } else if (index.eClasses.containsKey(uri)) { annotated = index.eClasses.get(uri); } else if (index.eAttributes.containsKey(uri)) { annotated = index.eAttributes.get(uri); } else if (index.eReferences.containsKey(uri)) { annotated = index.eReferences.get(uri); } else if (index.eDataTypes.containsKey(uri)) { annotated = index.eDataTypes.get(uri); } else if (index.eEnums.containsKey(uri)) { annotated = index.eEnums.get(uri); } else { log("Problem locating annotated element [" + uri + "]."); } if (annotated != null) { if ((baseComment != null) || (profileComment != null)) { /* Annotations with GenModel source are added to EMF generated code. */ EAnnotation genModelAnnotation = coreFactory.createEAnnotation(); genModelAnnotation.setSource("http://www.eclipse.org/emf/2002/GenModel"); if (baseComment != null) { EAnnotation baseAnnotation = coreFactory.createEAnnotation(); baseAnnotation.setSource(namespace); baseAnnotation.getDetails().put("Documentation", baseComment); annotated.getEAnnotations().add(baseAnnotation); genModelAnnotation.getDetails().put("Documentation", baseComment); } if (profileComment != null) { EAnnotation profileAnnotation = coreFactory.createEAnnotation(); profileAnnotation.setSource("http://langdale.com.au/2005/UML"); profileAnnotation.getDetails().put("Profile documentation", profileComment); annotated.getEAnnotations().add(profileAnnotation); genModelAnnotation.getDetails().put("Profile documentation", profileComment); } annotated.getEAnnotations().add(genModelAnnotation); } } } @Override protected void emitHeader(String uri, String label, String comment) { // TODO Auto-generated method stub } @Override protected void emitFlag(String uri) { // TODO Auto-generated method stub } @Override protected void emitImport(String uri) { // TODO Auto-generated method stub } public Index getIndex(){ return index; } }